#pragma once
#include "../type/CommonType.hpp"

//防止循环调用的声明

class IOContextPool;
namespace CommonData {

    /**
     * I/O上下文数据=============================================================================
     */
    class IOContext {
    public:
        // 声明友元类（全局命名空间的IOContextPool）
        friend class ::IOContextPool;

    public:

        //该I/O操作唯一标识符,相同的I/O操作，参数的地址相同，通过 GetQueuedCompletionStatus返回的 LPOVERLAPPED可反向获取 IOContext：
        OVERLAPPED m_overlapped;

        //描述数据缓冲区的信息,用于 WSARecv和 WSASend。
        WSABUF m_dataBuf;

        //I/O操作类型
        CommonType::IOCP::IO_OPERATION  m_operation;

        //缓冲区数据
        char m_buffer[CommonType::IOCP::BUFFER_SIZE];

        SOCKET m_socket;

    private:
        ~IOContext(){}

        IOContext() {
            ZeroMemory(&this->m_overlapped,sizeof(OVERLAPPED));
            this->m_dataBuf.len = CommonType::IOCP::BUFFER_SIZE;
            this->m_dataBuf.buf = this->m_buffer;
            this->m_operation = CommonType::IOCP::IO_OPERATION::READ;
            this->m_socket = INVALID_SOCKET;
        }

    public:


        /**
         *
         * @param other
         * @return
         */
        bool operator == (const IOContext& other) {
            return &this->m_overlapped == &other.m_overlapped;
        }

        /**
         *
         * @param other
         * @return
         */
        bool operator != (const IOContext& other) {
            return &this->m_overlapped != &other.m_overlapped;
        }

    };

    /**
     * 节点信息=================================================================================
     */
    class NodeInformation {
    private:
        SOCKET m_socket;
        std::string m_IPPortStr;

    public:
        NodeInformation() {
            this->m_socket = INVALID_SOCKET;
            this->m_IPPortStr = "";
        }

        /**
         *
         * @param IPPortStr
         * @param socket
         */
        NodeInformation(std::string& IPPortStr, SOCKET socket) {
            this->m_socket = socket;
            this->m_IPPortStr = IPPortStr;
        }

        ~NodeInformation() {}

        /**
         *
         * @return
         */
        std::string& getIPPortStr() {
            return this->m_IPPortStr;
        }


        /**
         *
         * @return
         */
        SOCKET& getSocket() {
            return this->m_socket;
        }


        /**
         *
         * @param socket
         */
        void setSocket(SOCKET& socket) {
            this->m_socket = socket;
        }

    };


    /**
     * 远程节点数据=============================================================================
     */
    class RemoteNodesData {
    private:
        std::unordered_map<std::string, std::shared_ptr<NodeInformation>> m_remoteNodes;
        std::unordered_map<SOCKET,std::string> m_remoteNodesSocketIPPortStrMap;
        std::mutex m_remoteNodesMx;

    public:
        RemoteNodesData() {}
        ~RemoteNodesData() {}

        /**
         * 是否存在节点
         * @param socket
         * @return
         */
        bool hashNode(SOCKET& socket) {
            auto it = this->m_remoteNodesSocketIPPortStrMap.find(socket);
            return it != this->m_remoteNodesSocketIPPortStrMap.end();
        }

        /**
         * 是否存在该节点
         * @param IPPortStr
         * @return
         */
        bool hashNode(const std::string& IPPortStr) {
            auto it = this->m_remoteNodes.find(IPPortStr);
            return it != this->m_remoteNodes.end();
        }

        /**
         *
         * @param IPPortStr
         * @return
         */
        SOCKET getNodeSocketByIPPortStr(std::string& IPPortStr) {

            auto it = this->m_remoteNodes.find(IPPortStr);

            if (it == this->m_remoteNodes.end()) return INVALID_SOCKET;

            return it->second->getSocket();
        }

        /**
         *
         * @param remoteNode
         */
        void addRemoteNode(std::shared_ptr<NodeInformation>& remoteNode) {
            std::lock_guard<std::mutex> lock(this->m_remoteNodesMx);
            if (remoteNode.get() == nullptr
                || remoteNode.get()->getIPPortStr() == ""
                || remoteNode.get()->getSocket() == INVALID_SOCKET) return;

            this->m_remoteNodesSocketIPPortStrMap[remoteNode.get()->getSocket()] = remoteNode.get()->getIPPortStr();
            this->m_remoteNodes[remoteNode.get()->getIPPortStr()] = remoteNode;
        }

        /**
         * 调用此函数会清除Socket
         * @param ipPortStr
         */
        void deleteRemeoteNodesByIPPortStr(const std::string& ipPortStr) {
            std::lock_guard<std::mutex> lock(this->m_remoteNodesMx);

            if (ipPortStr == "" || ipPortStr.empty()) return;

            auto it = this->m_remoteNodes.find(ipPortStr);
            //不存在该节点数据
            if (it == this->m_remoteNodes.end()) return;

            SOCKET socket = it->second.get()->getSocket();


            this->m_remoteNodesSocketIPPortStrMap.erase(socket);
            this->m_remoteNodes.erase(it);

            //清除Socket
            closesocket(socket);
        }


        /**
         * 调用此函数会清除Socket
         * @param socket
         */
        void deleteRemeoteNodesBySocket(SOCKET& socket) {
            std::lock_guard<std::mutex> lock(this->m_remoteNodesMx);
            if (socket == INVALID_SOCKET) return;

            auto it = this->m_remoteNodesSocketIPPortStrMap.find(socket);
            //不存在该节点数据
            if (it == this->m_remoteNodesSocketIPPortStrMap.end()) return;

            this->m_remoteNodes.erase(it->second);
            this->m_remoteNodesSocketIPPortStrMap.erase(it);

            //清除Socket
            closesocket(socket);
        }

        /**
         * 遍历处理节点
         * @param handleFun 传入的处理函数 返回false退出循环 返回true退出循环
         */
        void traverseHandleNodes(std::function<bool(std::shared_ptr<NodeInformation>&)> handleFun) {
            std::lock_guard<std::mutex> lock(this->m_remoteNodesMx);
            for (auto& pair : this->m_remoteNodes)
                if (!handleFun(pair.second)) return;
        }


        /**
         * 清除数据
         */
        void clear() {
            this->m_remoteNodesSocketIPPortStrMap.clear();
            this->m_remoteNodes.clear();
        }

    };

};